package grid;


import grid.items.Body;
import grid.items.Tile;

import java.awt.Point;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

import parameters.StaticParameters;

public class GridModel {
	private static final double DISTANCE_SENSING = 7;
	Tile[][] tiles;
	HashMap<Tile,Point>position=
		new HashMap<Tile,Point>();
	HashMap<String,Body>body=new HashMap<String, Body>();
	
	public GridModel(int x, int y)
	{
		tiles=new Tile[x][y];
	}
	
	public void move(Body b, Integer d) 
	{
		
			Point currentLocation=getPosition(b);
			Point nextLocation=null;
			if(d.equals(Direction.NORTH))
				nextLocation=new Point(currentLocation.x,currentLocation.y+1);
			if(d.equals(Direction.SOUTH))
				nextLocation=new Point(currentLocation.x,currentLocation.y-1);
			if(d.equals(Direction.EAST))
				nextLocation=new Point(currentLocation.x+1,currentLocation.y);
			if(d.equals(Direction.WEST))
				nextLocation=new Point(currentLocation.x-1,currentLocation.y);
			
			if(isOutside(nextLocation))
			{
				System.out.println(b+" tries to go out of the game !");
			}
			else
				if(isOccupied(nextLocation))
				{
					if(StaticParameters.WORLD_ERRORS_VERBOSITY)
						System.out.println(b+" cannot move to "+nextLocation);
				}
				else
					moveTo(b,nextLocation);
	}

	

	protected void remove(Set<Tile> tiles) {
		for(Tile t:tiles)
		{
			remove(t);
		}
	}
	protected void remove(Body b)
	{
		body.remove(b.getName());
		remove((Tile)b);
	}
	protected void remove(Tile t)
	{
		Point p=getPosition(t);
		tiles[p.x][p.y]=null;
		position.remove(t);
	}

	public Point getPosition(Tile t) {
		if(t instanceof Body)return getPosition((Body)t);
		for(int i=0;i<getDimensionX();i++)
			for(int j=0;j<getDimensionY();j++)
				if(getTile(new Point(i,j)).equals(t))return new Point(i,j);
		
		throw new Error();
	}

	protected Body getBody(Point p) {
		return (Body)tiles[p.x][p.y];
	}

	protected boolean isBody(Point p) {
		if(isEmpty(p))return true;
		return getTile(p) instanceof Body;
	}

	protected boolean moveTo(Body b, Point nextLocation) {
		if(!isEmpty(nextLocation))return false;
		Point current=getPosition(b);
		tiles[current.x][current.y]=null;
		tiles[nextLocation.x][nextLocation.y]=b;
		position.put(b, nextLocation);
		return true;
	}

	protected Point getPosition(Body b) {
		return position.get(b);
	}

	protected boolean isOccupied(Point p) {
		return tiles[p.x][p.y]!=null;
	}
	protected boolean isEmpty(int x, int y) {
		return isEmpty(new Point(x,y));
	}

	protected boolean isEmpty(Point point) {
		return !isOccupied(point);
	}

	private boolean isOutside(Point p) {
		return p.x<0||p.y<0||p.x>=tiles.length||p.y>=tiles[0].length;
	}

	public void enter(Body b, Point p) {
		if(isOccupied(p))
			System.out.println("agent "+b+" tries to enter in an occupied tile");
		else
		{
			tiles[p.x][p.y]=b;
			position.put(b, p);
			body.put(b.getName(),b);
		}
	}
	
	public String toString()
	{
		String res="";
		for(int i=tiles[0].length-1;i>=0;i--)
		{
			for(int j=0;j<tiles.length;j++)
				if(tiles[j][i]==null)
					res+=".";
				else res+=tiles[j][i].consolePrint();
			res+="\n";
		}
		return res;
	}

	public Set<Couple<Body,Point>> sense(Body b) {
		Set<Couple<Body, Point>>result=new HashSet<Couple<Body,Point>>();
		Point position=getPosition(b);
		for(int i=0;i<tiles.length;i++)
			for(int j=0;j<tiles[i].length;j++)
			{
				Point p=new Point(i,j);
				if(isOccupied(p)&&p.distance(position)<=DISTANCE_SENSING)
					result.add(new Couple<Body,Point>(getBody(p),p));
			}
		return result;
	}

	protected Body getBody(String name) {
		return body.get(name);
	}

	public void putItem(Point point, Tile t) {
		tiles[point.x][point.y]=t;
	}

	public int getDimensionX() {
		return tiles.length;
	}

	public int getDimensionY() {
		return tiles[0].length;
	}

	public Tile getTile(int x, int y) {
		return tiles[x][y];
	}
	protected Tile getTile(Point p) {
		return getTile(p.x,p.y);
	}
	


	protected void addItem(int x, int y, Tile t) {
		putItem(new Point(x,y), t);
		if(t instanceof Body)
		{
			Body b=(Body)t;
			body.put(b.getName(),b);
		}
		position.put(t,new Point(x,y));
		
	}


	protected void clearTile(Point p) {
		tiles[p.x][p.y]=null;
	}
}
